home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 1 / PC Actual CD 01.iso / trucos / utils / util.cd < prev    next >
Encoding:
Text File  |  1995-01-03  |  38.5 KB  |  2,191 lines

  1.  
  2.  
  3. UTILIDADES
  4.  
  5. CARGADOR DE EJECUTABLES
  6.  
  7. Esta rutina no es más que un cargador/reubicador de ficheros EXE. Los
  8. creadores del DOS no debieron pensar en su día que en ciertas
  9. ocasiones la carga de ficheros EXE mediante funciones del sistema no
  10. se ajusta a las necesidades de ciertas aplicaciones, como por ejemplo
  11. aquellas que requieran cargar un fichero EXE desde dentro de otro
  12. fichero.
  13.  
  14. Esta rutina subsana este problema, ya que permite cargar ficheros y
  15. especificar en que offset dentro de éstos se encuentra el EXE (para
  16. una carga normal de ficheros EXE se usa un offset de 0 y ya está). No
  17. puede cargar ficheros con overlays ni ficheros COM, y aunque no
  18. funciona en el 100% de los casos, su efectividad es bastante alta.
  19.  
  20. Las rutinas son para Turbo Pascal 7.0. Se compila primero la unidad
  21. «Exec» y después el fichero «Exectest». Su uso es sencillo:
  22.  
  23. EXECTEST fichero.exe
  24.  
  25. La unidad «exec» es la que sigue:
  26.  
  27. UNIT Exec;
  28.  
  29. INTERFACE
  30.  
  31. USES
  32.  
  33.  Dos;
  34.  
  35. VAR
  36.  
  37.  FicheroExe : STRING;
  38.  
  39.  header: ARRAY [1..28] OF BYTE;
  40.  
  41.  handle,PSPSegment,StartSegment:WORD;
  42.  
  43.  item: ARRAY [1..2] OF WORD;
  44.  
  45.  memory,imagesize:WORD;
  46.  
  47. PROCEDURE ExecFile(pos:LONGINT);
  48.  
  49.  
  50. IMPLEMENTATION
  51.  
  52. { convierte un string en formato ASCIIZ para utilizarlo con }
  53.  
  54. { las funciones del DOS }
  55.  
  56. PROCEDURE StringToAsciiz; ASSEMBLER;
  57.  
  58. ASM
  59.  
  60.  mov bx,OFFSET FicheroExe
  61.  
  62.  xor cx,cx
  63.  
  64.  mov cl,[bx]
  65.  
  66. @@StringToASCII:
  67.  
  68.  mov al,[bx+1]
  69.  
  70.  mov [bx],al
  71.  
  72.  inc bx
  73.  
  74.  loop @@StringToASCII
  75.  
  76.  mov byte ptr [bx],0
  77.  
  78. END;
  79.  
  80. { Carga, reubica y ejecuta un fichero .EXE }
  81.  
  82. PROCEDURE ExecFile(pos:LONGINT); ASSEMBLER;
  83.  
  84. ASM
  85.  
  86.  push es
  87.  
  88.  call StringToAsciiz { convierte el nombre del .exe a ASCIIZ }
  89.  
  90.  push ds
  91.  
  92.  mov ax,3d00h
  93.  
  94.  mov dx,OFFSET FicheroExe
  95.  
  96.  int 21h { abre el fichero para sólo lectura }
  97.  
  98.  { indexado del fichero }
  99.  
  100.  push ax
  101.  
  102.  mov bx,ax { handle del fichero }
  103.  
  104.  mov ax,04200h
  105.  
  106.  mov dx,word ptr [pos]
  107.  
  108.  mov cx,word ptr [pos+2]
  109.  
  110.  int 21h
  111.  
  112.  pop ax
  113.  
  114.  mov [handle],ax { guarda el handle del fichero }
  115.  
  116.  mov bx,[handle]
  117.  
  118.  mov dx,OFFSET header { cabecera del fichero .exe }
  119.  
  120.  mov cx,28
  121.  
  122.  mov ah,3fh
  123.  
  124.  int 21h { lee los 28 bytes estándar de la cabecera }
  125.  
  126.  pop ds
  127.  
  128.  mov bx,OFFSET header
  129.  
  130.  mov cx,[bx+08h] { longitud del header en parágrafos }
  131.  
  132.  mov ax,16
  133.  
  134.  imul cx { DX:AX -> posición de inicio de módulo }
  135.  
  136.  mov cx,dx
  137.  
  138.  mov dx,ax { CX:DX -> posición para el LSEEK }
  139.  
  140.  add dx,word ptr [pos]
  141.  
  142.  adc cx,word ptr [pos+2]
  143.  
  144.  mov bx,[handle]
  145.  
  146.  xor al,al
  147.  
  148.  mov ah,42h
  149.  
  150.  int 21h
  151.  
  152. { posiciona el puntero del fichero al inicio del módulo del programa }
  153.  
  154.  mov bx,OFFSET header
  155.  
  156.  mov ax,[bx+4] { longitud total incluyendo la cabecera }
  157.  
  158.  inc ax { 1+ por si acaso }
  159.  
  160.  mov cx,32
  161.  
  162.  mul cx { parágrafos totales }
  163.  
  164.  mov [imagesize],ax { tamaño del modulo del programa }
  165.  
  166.  mov bx,0ffffh
  167.  
  168.  mov ah,48h
  169.  
  170.  int 21h
  171.  
  172.  jnc @@no_error0
  173.  
  174.  mov [memory],bx
  175.  
  176.  mov ah,48h
  177.  
  178.  int 21h { con esto reservamos el máximo de memoria }
  179.  
  180. @no_error0:
  181.  
  182.  mov [PSPSegment],ax
  183.  
  184.  { genera un nuevo PSP...UNDOCUMENTED! }
  185.  
  186.  push ax
  187.  
  188.  mov dx,ax
  189.  
  190.  mov ah,055h
  191.  
  192.  mov si,dx
  193.  
  194.  add si,10h
  195.  
  196.  add si,[memory]
  197.  
  198.  { valor a escribir en el campo de memoria del PSP }
  199.  
  200.  int 21h
  201.  
  202.  pop ax
  203.  
  204.  mov es,ax
  205.  
  206.  xor di,di
  207.  
  208.  mov word ptr es:[di+0ah],OFFSET @@retorna
  209.  
  210.  mov es:[di+0ch],cs
  211.  
  212.  add ax,16
  213.  
  214.  mov [StartSegment],ax
  215.  
  216.  { aquí nos encargamos de leer el módulo principal del programa, que }
  217.  
  218.  { no es más que el código y los datos en sí... }
  219.  
  220.  mov bx,[handle]
  221.  
  222.  mov cx,[imagesize]
  223.  
  224.  push ds
  225.  
  226.  mov ds,[StartSegment]
  227.  
  228. @@next_block:
  229.  
  230.  push cx
  231.  
  232.  xor dx,dx
  233.  
  234.  mov cx,16 { 16 bytes cada bloque }
  235.  
  236.  mov ah,3fh
  237.  
  238.  xor al,al
  239.  
  240.  int 21h
  241.  
  242.  jc @@END_read
  243.  
  244.  mov ax,ds
  245.  
  246.  inc ax
  247.  
  248.  mov ds,ax
  249.  
  250.  pop cx
  251.  
  252.  loop @@next_block
  253.  
  254.  pop ds
  255.  
  256.  jmp @@readgood
  257.  
  258. @@END_read:
  259.  
  260.  pop cx
  261.  
  262.  pop ds
  263.  
  264. @@readgood:
  265.  
  266.  { ahora posiciona el puntero del fichero al inicio de la tabla de }
  267.  
  268.  { reubicación del fichero .EXE }
  269.  
  270.  mov si,OFFSET header
  271.  
  272.  mov ax,[si+18h]
  273.  
  274.  { offset en bytes en el fichero de la tabla de reubicación del .exe }
  275.  
  276.  mov dx,ax
  277.  
  278.  xor cx,cx
  279.  
  280.  add dx,word ptr [pos] { más la posición }
  281.  
  282.  adc cx,word ptr [pos+2]
  283.  
  284.  mov bx,[handle]
  285.  
  286.  mov ah,42h
  287.  
  288.  xor al,al
  289.  
  290.  int 21h
  291.  
  292.  { ahora comienza la reubicación de verdad del fichero }
  293.  
  294.  mov si,OFFSET header
  295.  
  296.  mov cx,[si+06h] { número de datos de items de la tabla de reubicación }
  297.  
  298.  test cx,0ffffh
  299.  
  300.  jz @@no_realocate
  301.  
  302. @@siguiente_item:
  303.  
  304.  push cx
  305.  
  306.  mov dx,OFFSET item
  307.  
  308.  mov bx,[handle]
  309.  
  310.  mov cx,4 { cada item se compone de offset:segmento -> 4 bytes }
  311.  
  312.  mov ah,3fh
  313.  
  314.  int 21h
  315.  
  316.  mov bx,OFFSET item
  317.  
  318.  mov ax,[StartSegment]
  319.  
  320.  { a cada posición indicada por el item se
  321.  
  322.  le suma el segmento de inicio del módulo }
  323.  
  324.  add ax,[bx+2]
  325.  
  326.  mov di,[bx]
  327.  
  328.  mov es,ax
  329.  
  330.  mov ax,es:[di]
  331.  
  332.  add ax,[StartSegment]
  333.  
  334.  mov es:[di],ax
  335.  
  336.  pop cx
  337.  
  338.  loop @@siguiente_item
  339.  
  340. @@no_realocate:
  341.  
  342.  mov bx,[handle]
  343.  
  344.  mov ah,3eh
  345.  
  346.  int 21h { cierra el fichero }
  347.  
  348.  { set new PSP address...UNDOCUMENTED! }
  349.  
  350.  mov bx,[PSPSegment]
  351.  
  352.  mov ah,50h
  353.  
  354.  int 21h
  355.  
  356.  mov bx,OFFSET @@oldss
  357.  
  358.  cli
  359.  
  360.  mov cs:[bx],ss
  361.  
  362.  mov cs:[bx+2],sp
  363.  
  364.  mov cs:[bx+4],bp
  365.  
  366.  sti
  367.  
  368.  mov bx,OFFSET header
  369.  
  370.  mov ax,[bx+0eh]
  371.  
  372.  add ax,[StartSegment]
  373.  
  374.  cli
  375.  
  376.  mov ss,ax
  377.  
  378.  mov sp,[bx+10h] { genera SS:SP a partir del header }
  379.  
  380.  sti
  381.  
  382.  mov ax,[bx+14h]
  383.  
  384.  mov dx,[bx+16h]
  385.  
  386.  add dx,[StartSegment] { DX:AX -> CS:IP }
  387.  
  388.  push dx
  389.  
  390.  push ax
  391.  
  392.  mov es,[PSPSegment]
  393.  
  394.  mov ds,[PSPSegment]
  395.  
  396.  xor ax,ax { borra todos los registros }
  397.  
  398.  mov di,ax
  399.  
  400.  mov si,ax
  401.  
  402.  mov bx,ax
  403.  
  404.  mov cx,ax
  405.  
  406.  mov dx,ax
  407.  
  408.  mov bp,ax
  409.  
  410.  retf
  411.  
  412. @@retorna:
  413.  
  414.  mov bx,OFFSET @@oldss
  415.  
  416.  cli
  417.  
  418.  mov ss,cs:[bx] { recupera todos los registros }
  419.  
  420.  mov sp,cs:[bx+2]
  421.  
  422.  mov bp,cs:[bx+4]
  423.  
  424.  sti
  425.  
  426.  mov ax,SEG @data
  427.  
  428.  mov ds,ax
  429.  
  430.  mov es,[PSPSegment]
  431.  
  432.  mov ah,49h
  433.  
  434.  int 21h { libera la memoria guardada por el }
  435.  
  436.  jmp @@restore
  437.  
  438. @@oldss: dw 0
  439.  
  440. @@oldsp: dw 0
  441.  
  442. @@oldbp: dw 0
  443.  
  444. @@restore:
  445.  
  446.  pop es
  447.  
  448. END;
  449.  
  450. BEGIN
  451.  
  452. END.
  453.  
  454.  
  455. El programa «Exectest» es el sigiente:
  456.  
  457.  
  458. {$M,16000,0,0}
  459.  
  460. {$F+}
  461.  
  462. USES Dos, Crt, Exec;
  463.  
  464. { devuelve la memoria que queda libre }
  465.  
  466. FUNCTION MemoriaLibre: WORD; ASSEMBLER;
  467.  
  468. ASM
  469.  
  470.  mov bx,0ffffh
  471.  
  472.  mov ah,48h
  473.  
  474.  int 21h
  475.  
  476.  mov ax,bx
  477.  
  478. END;
  479.  
  480. { programa principal }
  481.  
  482. BEGIN
  483.  
  484.  WriteLn('Exec loader version 2.11');
  485.  
  486.  IF paramcount=0 THEN
  487.  
  488.  BEGIN
  489.  
  490.  WriteLn('Usage <filename.exe>');
  491.  
  492.  Halt;
  493.  
  494.  END;
  495.  
  496.  Write('Memoria libre antes de ejecutar el programa:
  497.  ',LongInt(MemoriaLibre)*16,' bytes');
  498.  
  499.  FicheroExe:=paramstr(1);
  500.  
  501.  ExecFile(0);
  502.  
  503.  ASM
  504.  
  505.  mov ax,03
  506.  
  507.  int $10
  508.  
  509.  END;
  510.  
  511.  Write('Memoria libre después de salir del programa:
  512.  ',LongInt(MemoriaLibre)*16,' bytes');
  513.  
  514. END.
  515.  
  516. José Cabezas García
  517.  
  518. Barcelona
  519.  
  520.  
  521. ACTUALIZACION DEL PATH
  522.  
  523. El siguiente programa tiene como finalidad el añadir o actualizar
  524. directorios en la variable de entorno «Path» de forma temporal, es
  525. decir, hasta que se apague el ordenador, o bien de forma definitiva
  526. añadiéndolo automáticamente en el «autoexec.bat».
  527.  
  528. Para añadir directorios de forma temporal basta con pasarle como
  529. parámetros los directorios a añadir separados por espacios o por punto
  530. y coma (;). Si queremos que los cambios sean permanentes, esto es,
  531. que se actualice el «autoexec.bat», basta con poner el modificador /A
  532. al final del comando, justo después de los nuevos directorios que
  533. queremos añadir.
  534.  
  535. @Echo off
  536.  
  537. if "%1"=="" goto sin_args
  538.  
  539. :loop
  540.  
  541. if "%1"=="" goto fin_ok
  542.  
  543. if "%1"=="/A" goto permanente
  544.  
  545. if "%1"=="/a" goto permanente
  546.  
  547. set path=%path%;%1
  548.  
  549. shift
  550.  
  551. goto loop
  552.  
  553. :permanente
  554.  
  555. echo set path=%path% >> c:\autoexec.bat
  556.  
  557. goto fin
  558.  
  559. :sin_args
  560.  
  561. echo Sintaxis: MPATH camino [camino] ...
  562.  
  563. echo Para modificar AUTOEXEC.BAT: MPATH camino [camino] ... /A
  564.  
  565. goto final
  566.  
  567. :fin_ok
  568.  
  569. echo Nuevo PATH=%Path%
  570.  
  571. :final
  572.  
  573. Luis Rodríguez Beteta
  574.  
  575. Madrid
  576.  
  577.  
  578.  
  579. CAPTURADOR DE PANTALLAS ASCII
  580.  
  581. El siguiente truco, realizado con Turbo Pascal, es un capturador de
  582. pantallas ASCII que ocupa menos de 10 Kbytes de memoria y cuyo
  583. funcionamiento se basa en que cuando se instala en memoria captura la
  584. interrupción del teclado.
  585.  
  586. Para capturar una pantalla basta con pulsar la combinación «Shift-F2»,
  587. lo que dará lugar a que el programa genere un archivo llamado
  588. «Capt-XX.txt», donde XX es sustituido por un 00, 01, 02... que
  589. equivale al número de pantalla capturada.
  590.  
  591. {$M 2000, 0, 0}
  592.  
  593. {$R-, S-, I-, D+, F+,V-, V-, B-, N-, L-}
  594.  
  595. Program Capturador;
  596.  
  597. Uses Dos;
  598.  
  599. Const IntTec = $09;
  600.  
  601. Type Matriz = Array [1..2000] Of Byte;
  602.  
  603. Var
  604.  
  605.  VecTecAnt : Pointer;
  606.  
  607.  Buffer : Matriz;
  608.  
  609.  Archivo : File Of Matriz;
  610.  
  611.  XW1, XW2, XW3 : Word;
  612.  
  613.  XS1 : Strig;
  614.  
  615. Procedure CapturaPantalla;
  616.  
  617. Begin
  618.  
  619.  If Mem[Seg0040:$0049]=7 Then XW1:=$B000 Else XW1:=$B800;
  620.  
  621.  XW3:=1;
  622.  
  623.  For XW2:=0 To 3999 Do
  624.  
  625.  Begin
  626.  
  627.  Buffer[XW3]:=Mem[XW1:XW2];
  628.  
  629.  Inc(XW2);
  630.  
  631.  Inc(XW3);
  632.  
  633.  End;
  634.  
  635.  XW1:=0;
  636.  
  637.  Repeat
  638.  
  639.  Str(Xw1:2,XS1);
  640.  
  641.  If XS1[1]=#32 Then XS1[1]:='0';
  642.  
  643.  XS1:='CAPT-'+XS1+'.TXT';
  644.  
  645.  Assign (Archivo,XS1);
  646.  
  647.  Reset(Archivo);
  648.  
  649.  If IOResult = 0 The Close(Archivo)
  650.  
  651.  Else
  652.  
  653.  Begin
  654.  
  655.  Rewrite(Archivo);
  656.  
  657.  Write(Archivo, Buffer);
  658.  
  659.  Close(archivo);
  660.  
  661.  Exit;
  662.  
  663.  End;
  664.  
  665.  Inc(XW1);
  666.  
  667.  Until False;
  668.  
  669. End;
  670.  
  671. Procedure Teclado (Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES,
  672. BP:Word); Interrupt;
  673.  
  674. Begin
  675.  
  676.  Asm
  677.  
  678.  pushf
  679.  
  680.  call VecTecAnt
  681.  
  682.  End;
  683.  
  684.  If Port[96]=60 Then CapturaPantalla;
  685.  
  686.  Inline($FB);
  687.  
  688. End;
  689.  
  690. Begin
  691.  
  692.  WriteLn('Capturador de pantallas ASCII');
  693.  
  694.  WriteLn('Para capturar la pantalla pulsar Shift-F2');
  695.  
  696.  GetIntVec(IntTec,VecTecAnt);
  697.  
  698.  SetIntVec(IntTec,@Teclado);
  699.  
  700.  Keep(0);
  701.  
  702. End.
  703.  
  704. Bernardo Calero Castellano
  705.  
  706. Tomelloso (Ciudad Real)
  707.  
  708. Nota del Laboratorio: La idea de esta utilidad consiste en visualizar
  709. un cursor en pantalla y que podamos moverlo y situarlo donde queramos.
  710. En este punto apretaremos «ENTER» y podremos seguir moviendo el cursor
  711. para señalar los dos vértices de la zona a capturar. Una vez elegida
  712. la zona bastará grabar el contenido de las posiciones de memoria que
  713. se encuentran entre los dos vértices.
  714.  
  715.  
  716. MENSAJES POR VOZ
  717.  
  718. Muchas veces resulta interesante dejar una nota a la siguiente persona
  719. que va a utilizar el ordenador, o simplemente un recordatorio para
  720. nosotros mismos. Sin embargo, lo ideal sería poder dejar un mensaje
  721. de viva voz. Pues bien, eso es lo que hace el programa
  722. «mensajes.bat».
  723.  
  724. Para su correcto funcionamiento es imprescindible que se cumplan los
  725. siguientes requisitos:
  726.  
  727. 1. El programa está ideado, en principio, para los poseedores de una
  728. tarjeta de sonido Sound Blaster, aunque también se pueden dejar
  729. mensajes por escrito.
  730.  
  731. 2. Requiere la versión 6.0 ó posteriór de MS-DOS (ya que utiliza el
  732. comando CHOICE).
  733.  
  734. 3. Se debe instalar el controlador «ANSI.SYS» (o en caso contrario
  735. habrá que suprimir las secuencias de escape, ya que sólo sirven para
  736. cambiar el color de la pantalla).
  737.  
  738. 4. Los programas «VPLAY» y «VREC» de la Sond Blaster deben estar
  739. disponibles mediante la variable de entorno PATH. (También se puede
  740. especificar la ruta de acceso cada vez que aparezca una llamada a
  741. cualquiera de estos dos programas)
  742.  
  743. 5. El programa debe ser instalado en un subdirectorio llamado
  744. «mensajes».
  745.  
  746. Si ejecutamos la aplicación desde la línea de comandos se nos
  747. presentará un menú desde el que podremos grabar mensajes escritos o
  748. hablados, escuchar el mensaje pregrabado o borrar el mensaje
  749. almacenado.
  750.  
  751. Si arrancamos la utilidad con la opción r («mensajes r») o incluyendo
  752. la llamada «call c:\mensajes\mensajes.bat r» en el «autoexec.bat», nos
  753. recordará los mensajes hablados y/o escritos (si es que existen) y
  754. después nos dará la oportunidad de volver a escucharlos o borrarlos.
  755. En caso de que no exista ningún mensaje esta llamada no tiene ningún
  756. efecto.
  757.  
  758. @echo off
  759.  
  760. if "%1" =="r" goto principio
  761.  
  762. if "%1" =="R" goto principio
  763.  
  764. :principal
  765.  
  766. rem ** Rutina principal **
  767.  
  768. rem **********************
  769.  
  770. echo 
  771.  
  772. cls
  773.  
  774. echo  MENSAJES.BAT 
  775.  
  776. echo.
  777.  
  778. echo OPCIONES
  779.  
  780. echo.
  781.  
  782. echo [1] Grabar mensaje de texto
  783.  
  784. echo [2] Grabar mensaje de voz
  785.  
  786. echo.
  787.  
  788. echo [3] Escuchar mensaje
  789.  
  790. echo.
  791.  
  792. echo [4] Borrar mensaje
  793.  
  794. echo.
  795.  
  796. echo [0] SALIR
  797.  
  798. echo.
  799.  
  800. choice Opción /c:01234
  801.  
  802. if errorlevel 5 goto borra
  803.  
  804. if errorlevel 4 goto principio
  805.  
  806. if errorlevel 3 goto grabavoz
  807.  
  808. if errorlevel 2 goto grabatexto
  809.  
  810. if errorlevel 1 goto end
  811.  
  812. :Grabavoz
  813.  
  814. cls
  815.  
  816. vrec mensaje /s:11000
  817.  
  818. goto principal
  819.  
  820. :Grabatexto
  821.  
  822. edit mensaje.txt
  823.  
  824. goto principal
  825.  
  826. rem ** Rutina que recuerda los mensajes y sale del programa **
  827.  
  828. rem **********************************************************
  829.  
  830. :principio
  831.  
  832. if exist c:\mensajes\mensaje.* goto rutina
  833.  
  834. echo MENSAJES.BAT: No hay mensajes para usted
  835.  
  836. echo.
  837.  
  838. if "%1" =="r" goto End
  839.  
  840. if "%1" =="R" goto End
  841.  
  842. goto principal
  843.  
  844. :rutina
  845.  
  846. echo 
  847.  
  848. cls
  849.  
  850. echo  MENSAJES.BAT 
  851.  
  852. echo Hay un mensaje para usted:
  853.  
  854. echo.
  855.  
  856. if not exist c:\mensajes\mensaje.txt goto voz
  857.  
  858. type c:\mensajes\mensaje.txt|more
  859.  
  860. :voz
  861.  
  862. echo 
  863.  
  864. if exist c:\mensajes\mensaje.voc vplay c:\mensajes\mensaje>nul
  865.  
  866. if exist c:\mensajes\mensaje.voc choice ¿Desea que le repita el
  867. mensaje
  868.  
  869. if not exist c:\mensajes\mensaje.voc pause
  870.  
  871. if errorlevel 2 goto sigue
  872.  
  873. if errorlevel 1 goto principio
  874.  
  875. :sigue
  876.  
  877. choice ¿Borrar mensaje
  878.  
  879. if errorlevel 2 goto continua
  880.  
  881. if errorlevel 1 goto borra
  882.  
  883. :Borra
  884.  
  885. echo Borrando mensaje... Espere por favor
  886.  
  887. del c:\mensajes\mensaje.txt>nul
  888.  
  889. del c:\mensajes\mensaje.voc>nul
  890.  
  891. if "%1" =="r" goto continua
  892.  
  893. if "%1" =="R" goto continua
  894.  
  895. goto principal
  896.  
  897. :continua
  898.  
  899. if "%1" =="r" goto End
  900.  
  901. if "%1" =="R" goto End
  902.  
  903. goto principal
  904.  
  905. :end
  906.  
  907. echo 
  908.  
  909. cls
  910.  
  911. Gorka Elexgaray
  912.  
  913. Guernica. (Vizcaya)
  914.  
  915.  
  916. PARTICION DE FICHEROS
  917.  
  918. En muchas ocasiones nos encontramos ante un fichero de texto cuyas
  919. dimensiones hacen imposible que sea editado. Mediante esta utilidad
  920. escrita en Turbo Pascal, podremos dividir cualquier fichero en
  921. diversos ficheros del numero de líneas que deseemos. Por defecto se
  922. toma el número de 5.000 líneas por fichero.
  923.  
  924. {$i-}
  925.  
  926. Program Partidor;
  927.  
  928. Var
  929.  
  930.  Origen,
  931.  
  932.  Final :Text;
  933.  
  934.  No_de_lineas,
  935.  
  936.  Actual :LongInt;
  937.  
  938.  FichF :String[5];
  939.  
  940.  NoArch,
  941.  
  942.  Codigo :Integer;
  943.  
  944.  Item :String;
  945.  
  946.  No_fichero :Byte;
  947.  
  948. Begin
  949.  
  950.  No_de_lineas:=5000;
  951.  
  952.  If ParamCount>2 then Val(ParamStr(3), No_de_Lineas, Codigo);
  953.  
  954.  If (ParamCount<2) Or (Codigo<>0) then
  955.  
  956.  begin
  957.  
  958.  WriteLn('Usar: [unidad:][camino\]Partidor [unidad:]'+
  959.  
  960.  '[camino\]Arch_Original.ext Arch_final');
  961.  
  962.  Halt;
  963.  
  964.  End;
  965.  
  966.  FichF:=Copy(ParamStr(2),1,5);
  967.  
  968.  If No_de_lineas<512 then No_de_lineas:=512;
  969.  
  970.  Assign(Origen,ParamStr(1));
  971.  
  972.  Reset(Origen);
  973.  
  974.  If IOResult<>0 then
  975.  
  976.  begin
  977.  
  978.  WriteLn('Imposible abrir ',ParamStr(1));
  979.  
  980.  Halt(1);
  981.  
  982.  End;
  983.  
  984.  WriteLn('Fragmentando '+ParamStr(1)+'...');
  985.  
  986.  No_Fichero:=0;
  987.  
  988.  Repeat;
  989.  
  990.  Actual:=0;
  991.  
  992.  Inc(No_Fichero);
  993.  
  994.  Str(No_Fichero,Item);
  995.  
  996.  While Length(item)<3 do
  997.  
  998.  Item:='0'+Item;
  999.  
  1000.  Assign(Final,ParamStr(2)+'.'+Item);
  1001.  
  1002.  Rewrite(Final);
  1003.  
  1004.  If IOResult<>0 then
  1005.  
  1006.  begin
  1007.  
  1008.  WriteLn('Imposible crear '+ParamStr(2)+'.'+Item);
  1009.  
  1010.  Halt(1);
  1011.  
  1012.  End;
  1013.  
  1014.  While Not Eof(Origen) do
  1015.  
  1016.  begin
  1017.  
  1018.  ReadLn(Origen,Item);
  1019.  
  1020.  WriteLn(Final,Item);
  1021.  
  1022.  Inc(Actual);
  1023.  
  1024.  If Actual=No_de_lineas then break;
  1025.  
  1026.  End;
  1027.  
  1028.  Close(Final);
  1029.  
  1030.  Until Eof(Origen);
  1031.  
  1032.  Close(Origen);
  1033.  
  1034.  WriteLn('Archivo '+ParamStr(1)+' fragmentado en ',No_Fichero,'Archivos.');
  1035.  
  1036. End.
  1037.  
  1038. Los consejos del Laboratorio:
  1039.  
  1040. - Como ya hemos comentado, la utilidad de este programa radica en
  1041. poder dividir ficheros de texto que a causa de su tamaño no son
  1042. editables, en otros de menor tamaño. Una vez que hemos realizado los
  1043. cambios deseados sobre cada unos de los ficheros más pequeños, puede
  1044. que nos interese volver a juntar dichos archivos para tener de nuevo
  1045. un único fichero. Para juntar todos los ficheros podemos recurrir a
  1046. la orden «copy» del DOS, de la siguiente forma: «copy
  1047. fich1+fich2+...+fichn fichfin»
  1048.  
  1049. Luciano Rubio
  1050.  
  1051. Madrid
  1052.  
  1053.  
  1054. PARTICION DE FICHEROS MEJORADA
  1055.  
  1056. En el truco anterior hablábamos de una utilidad que servía para
  1057. descomponer ficheros de texto en otros de menor tamaño, para de esta
  1058. forma poder ser editados con facilidad. Esta nueva utilidad, aunque
  1059. básicamente hace lo mismo, tiene la ventaja de poder utilizarse tanto
  1060. con ficheros de texto como en ficheros binarios.
  1061.  
  1062. Su utilidad es clara, si tenemos un ejecutable que sobrepase la
  1063. capacidad de nuestros disquetes, podemos dividirlo en diferentes
  1064. ficheros binarios los cuales puedan ser grabados en disquetes, para
  1065. luego resconstruirlos a su formato original.
  1066.  
  1067. La sintaxis de uso la indica el propio programa, así como el tamaño y
  1068. numero de ficheros creados. De igual forma, indica como regenerar el
  1069. fichero original a partir de los trozos.
  1070.  
  1071. program trocear;
  1072.  
  1073.  uses dos,crt;
  1074.  
  1075. var
  1076.  
  1077.  fuente,destino:file of byte;
  1078.  
  1079.  i,veces,e:integer;
  1080.  
  1081.  tamano,c,tamanototal,j:longint;
  1082.  
  1083.  b:string[10];
  1084.  
  1085.  nombre,nombreb,nombreorigen:string[9];
  1086.  
  1087.  extension:string[3];
  1088.  
  1089.  letra:byte;
  1090.  
  1091.  fin:boolean;
  1092.  
  1093. begin
  1094.  
  1095.  fin:=false;
  1096.  
  1097.  if paramstr(1)='' then
  1098.  
  1099.  begin
  1100.  
  1101.  writeln('troceador por ramón bernardo 93');
  1102.  
  1103.  writeln;
  1104.  
  1105.  writeln('sintaxis: trocear fichero.ext tamaÑo');
  1106.  
  1107.  writeln;
  1108.  
  1109.  writeln('donde tamaño está entre 1024 y 2147483647 bytes');
  1110.  
  1111.  writeln;
  1112.  
  1113.  writeln('por defecto el tamaño es de 360 kb');
  1114.  
  1115.  halt(1);
  1116.  
  1117.  end
  1118.  
  1119.  else
  1120.  
  1121.  begin
  1122.  
  1123.  {$i+}
  1124.  
  1125.  nombreorigen:=paramstr(1);
  1126.  
  1127.  if paramstr(2)='' then b:='367616'
  1128.  
  1129.  else b:=paramstr(2);
  1130.  
  1131.  val(b,tamano,e);
  1132.  
  1133.  if tamano<1024 then
  1134.  
  1135.  begin
  1136.  
  1137.  writeln('error:el tamaño de los subficheros ha de ser mayor de 1024');
  1138.  
  1139.  halt(1);
  1140.  
  1141.  end;
  1142.  
  1143.  tamano:=1024*(tamano div 1024);
  1144.  
  1145.  assign(fuente,paramstr(1));
  1146.  
  1147.  reset(fuente);
  1148.  
  1149.  nombreb:='';
  1150.  
  1151.  nombre:='';
  1152.  
  1153.  for j:=1 to length(nombreorigen) do
  1154.  
  1155.  if nombreorigen[j]='.' then j:=length(nombreorigen)
  1156.  
  1157.  else nombreb:=concat(nombreb,nombreorigen[j]);
  1158.  
  1159.  veces:=0;
  1160.  
  1161.  repeat
  1162.  
  1163.  str(veces,extension);
  1164.  
  1165.  repeat
  1166.  
  1167.  extension:=concat('0',extension);
  1168.  
  1169.  until length(extension)=3;
  1170.  
  1171.  nombre:=concat(nombreb,'.',extension);
  1172.  
  1173.  write('creando el fichero ',nombre);
  1174.  
  1175.  c:=0;
  1176.  
  1177.  assign(destino,nombre);
  1178.  
  1179.  rewrite(destino);
  1180.  
  1181.  while (not eof(fuente) and (c < tamano)) do
  1182.  
  1183.  begin
  1184.  
  1185.  read(fuente,letra);
  1186.  
  1187.  write(destino,letra);
  1188.  
  1189.  c:=c+1;
  1190.  
  1191.  end;
  1192.  
  1193.  writeln(' ',c,' bytes');
  1194.  
  1195.  if eof(fuente) then fin:=true;
  1196.  
  1197.  close(destino);
  1198.  
  1199.  veces:=veces+1;
  1200.  
  1201.  c:=0;
  1202.  
  1203.  until fin;
  1204.  
  1205.  close(fuente);
  1206.  
  1207.  writeln;
  1208.  
  1209.  writeln;
  1210.  
  1211.  writeln('para regenerar el fichero original teclear :');
  1212.  
  1213.  writeln;
  1214.  
  1215.  writeln('si el fichero original es ascii (texto)');
  1216.  
  1217.  writeln('copy ',nombreb,'.000+',nombreb,'.001+',nombreb,'.002+... ',nombreb,'.extension');
  1218.  
  1219.  writeln;
  1220.  
  1221.  writeln('si el fichero original es binario (exe,com,..)');
  1222.  
  1223.  writeln('copy ',nombreb,'.000 /b+',nombreb,'.001 /b+',nombreb,'.002 /b+... ',nombreb,'.extension');
  1224.  
  1225.  end;
  1226.  
  1227.  {$i-}
  1228.  
  1229. end.
  1230.  
  1231. Ramón B. de Bernardo Hernán
  1232.  
  1233. Sestao. Vizcaya
  1234.  
  1235.  
  1236. ORDENADOR PROTEGIDO
  1237.  
  1238. Más de una vez se habrá ausentado del ordenador durante unos minutos y
  1239. habrá querido bloquearlo para que nadie pudiera fisgar en él. O quizá
  1240. en algún momento deseó que desapareciera el contenido de la pantalla y
  1241. retornar a ella en cualquier momento. Este pequeño programa en C le
  1242. propone una solución.
  1243.  
  1244. Es una aplicación residente que se activa al pulsar una tecla o
  1245. combinación de ellas, salvando el contenido de la pantalla (modo
  1246. texto), borrandola y bloqueando el teclado. No se recuperará la
  1247. pantalla inicial hasta que se pulsa la combinación de teclas elegida.
  1248. La rutina sólo se instala una vez, ya que si se vuelve a hacer es
  1249. ignorada.
  1250.  
  1251. Las teclas que se usan para activarlo son Ctrl+Shift (izquierda) y
  1252. para desactivarlo Alt+F1. Estas teclas pueden ser otras, a gusto del
  1253. usuario, con el único problema de conocer el código correspondiente.
  1254. Para detectar la pulsación de Ctrl+Shift (izq) se buscó en el área de
  1255. comunicaciones de la BIOS en la dirección 0040:0017h (1 byte). Para
  1256. Alt+F1 se ha usado la interrupción 16h de la BIOS, comprobando el
  1257. código de exploración y el código ASCII correspondiente (si existe).
  1258. Si el código ASCII no existe (es cero), se examina el código de
  1259. exploración.
  1260.  
  1261. En el caso de las teclas Alt+F1 el código ASCII correspondiente es 00,
  1262. mientras que el código de exploración es 68h.
  1263.  
  1264. #include <Dos.h>
  1265.  
  1266. #include <Bios.h>
  1267.  
  1268. #include <Stdio.h>
  1269.  
  1270. unsigned int far *mem;
  1271.  
  1272. void interrupt ( *oldhandler)();
  1273.  
  1274. /* Función a la que se asigna la interrupción 9, que es la que
  1275. intercepta*/
  1276.  
  1277. void interrupt handler()
  1278.  
  1279. /* Función que reemplaza a la interrupción 9*/
  1280.  
  1281. {
  1282.  
  1283.  char ah, p_activa;
  1284.  
  1285.  static char activo = 0; /* Variable semáforo */
  1286.  
  1287.  enable(); /*Habilitar interrupciones hardware */
  1288.  
  1289.  oldhandler();
  1290.  
  1291.  if ((peekb(0x40,0x17) & 6)==6 && activo==0)
  1292.  
  1293.  /*Crtl+Shift(Izq) Pulsadas*/
  1294.  
  1295.  {
  1296.  
  1297.  activo = 1;
  1298.  
  1299.  /* Si volvemos a pulsar Crtl+Shtf(Izq) no tendrá efecto */
  1300.  
  1301.  _AH=0xf; /* Se obtiene la página de visualización activa */
  1302.  
  1303.  geninterrupt (0x10);
  1304.  
  1305.  p_activa=_BH;
  1306.  
  1307.  _AH=0x5; /* Se cambia de página activa */
  1308.  
  1309.  _AL=p_activa+3;
  1310.  
  1311.  geninterrupt (0x10);
  1312.  
  1313.  do
  1314.  
  1315.  {
  1316.  
  1317.  _AH=0; /* Se espera pulsación de tecla */
  1318.  
  1319.  geninterrupt (0x16);
  1320.  
  1321.  ah=_AH; /* Almacena código de exploración */
  1322.  
  1323.  }
  1324.  
  1325.  while(ah != 0x68); /* Mientras que no se pulse Alt+F1 */
  1326.  
  1327.  _AH=0x5; /* Se reestablece la página activa inicial */
  1328.  
  1329.  _AL=p_activa;
  1330.  
  1331.  geninterrupt (0x10);
  1332.  
  1333.  activo=0;
  1334.  
  1335.  }
  1336.  
  1337. }
  1338.  
  1339. void main()
  1340.  
  1341. {
  1342.  
  1343.  /* Se comprueba que la rutina sólo se instale una vez */
  1344.  
  1345.  if( (FP_OFF(getvect(0x9))) != (FP_OFF(handler)) )
  1346.  
  1347.  {
  1348.  
  1349.  oldhandler=getvect(0x9);
  1350.  
  1351.  /* Guarda la dirección de la interrupción 9 que está en la tabla de
  1352.  vectores de int */
  1353.  
  1354.  setvect(0x9,handler);
  1355.  
  1356.  /* Asigna la dirección de nuestra rutina al vector de interrupción
  1357.  de la int 9 */
  1358.  
  1359.  printf("*** Rutina instalada *** ");
  1360.  
  1361.  mem=MK_FP(_psp,0x2c);
  1362.  
  1363.  _AX=4900;
  1364.  
  1365.  _ES=*mem;
  1366.  
  1367.  geninterrupt(0x21); /*Elimina el bloque de entorno */
  1368.  
  1369.  keep(0,_SS + (_SP/16) - _PSP); /* Deja el programa residente */
  1370.  
  1371.  }
  1372.  
  1373. }
  1374.  
  1375. Los consejos del Laboratorio:
  1376.  
  1377. - Una forma de mejorar esta aplicación es introduciendo un pequeño
  1378. programa de configuración, de forma que al instalarse se pueda
  1379. modificar directamente las teclas que lo activan y lo desactivan.
  1380.  
  1381. - También se le puede meter una rutina para que acepte un
  1382. parámetro que haga que el programa se desinstale de la memoria.
  1383.  
  1384. Oscar Bernal González
  1385.  
  1386. Valladolid
  1387.  
  1388.  
  1389. LA LUZ DEL DISCO DURO
  1390.  
  1391. Mediante la siguiente rutina (escrita es Turbo Pascal 6.0) podemos
  1392. simular en pantalla el led de estado del disco duro. Esta simulación
  1393. puede representar grandes ventajas para aquellos usuarios que no
  1394. pueden visualizar dicho led, por tener la cpu debajo de la mesa, por
  1395. ejemplo.
  1396.  
  1397. program disklighter_v4;
  1398.  
  1399. {$m 1024,0,0}
  1400.  
  1401. (* reserva 1k para stack. *)
  1402.  
  1403. uses dos,crt;
  1404.  
  1405. type rutina_tratamiento=procedure;
  1406.  
  1407. const
  1408.  
  1409.  tiempo = 50;
  1410.  
  1411. var
  1412.  
  1413.  rutina_original :rutina_tratamiento;
  1414.  
  1415.  interrup_hd :byte;
  1416.  
  1417.  puntero,vector :pointer;
  1418.  
  1419. procedure nueva_rutina; interrupt;
  1420.  
  1421. var
  1422.  i :integer;
  1423.  
  1424. begin
  1425.  
  1426.  for i:=0 to tiempo do
  1427.  
  1428.  (* tiempo de luz. *)
  1429.  
  1430.  begin
  1431.  
  1432.  mem[$b800:156]:=ord('h');
  1433.  
  1434.  (* acceso a memoria de video. *)
  1435.  
  1436.  mem[$b800:157]:=$21;
  1437.  
  1438.  mem[$b800:158]:=ord('d');
  1439.  
  1440.  mem[$b800:159]:=$21;
  1441.  
  1442.  end;
  1443.  
  1444.  mem[$b800:157]:=$00;
  1445.  
  1446.  mem[$b800:159]:=$00;
  1447.  
  1448.  rutina_original;
  1449.  
  1450. end;
  1451.  
  1452. (* programa principal *)
  1453.  
  1454. begin
  1455.  
  1456.  writeln;
  1457.  
  1458.  writeln('disklighter v4.0 residente en memoria.');
  1459.  
  1460.  interrup_hd:=$76;
  1461.  
  1462.  getintvec(interrup_hd,vector);
  1463.  
  1464.  @rutina_original:=vector;
  1465.  
  1466.  setintvec(interrup_hd,@nueva_rutina);
  1467.  
  1468.  keep($00);
  1469.  
  1470. end.
  1471.  
  1472. Esta rutina intercepta la interrupción que actua sobre el contador de
  1473. interrupciones 8259, la cual se genera cada vez que se accede al disco
  1474. duro, y redirecciona el vector que apunta a la rutina de tratamiento
  1475. de dicha interrupción hacia esta nueva rutina, que visualiza en
  1476. pantalla «una luz», y reenlaza despues con la rutina de tratamiento
  1477. original.
  1478.  
  1479. Según la velocidad de la máquina en la que se instale, deberá darse un
  1480. valor diferente a la constante «Tiempo», por ejemplo, para un 386DX-25
  1481. un valor adecuado es 75, y para un 386DX-33 un valor adecuado es 210.
  1482. Si eligimos un valor de «Tiempo» demasiado bajo, apenas veremos la
  1483. «luz» y si se elige uno muiy alto el acceso al disco duro se
  1484. ralentiza. Es cuestión de probar el valor óptimo para nuestra
  1485. máquina, teniendo en cuenta que cuanto mayor sea la velocidad del
  1486. ordenador, mayor deberá ser el valor que buscamos.
  1487.  
  1488. Luis Gascón López
  1489.  
  1490. Valencia
  1491.  
  1492.  
  1493. LA FECHA DEL SISTEMA
  1494.  
  1495. En ocasiones puede ser útil la visualización de la fecha del día, pero
  1496. la instrucción «Time» del DOS no es lo más adecuado para ello. Este
  1497. pequeño programa en C, nos permite la visualización de la fecha en su
  1498. formato correcto.
  1499.  
  1500. #include <stdio.h>
  1501.  
  1502. #include <dos.h>
  1503.  
  1504. #include <time.h>
  1505.  
  1506. void main()
  1507.  
  1508. {
  1509.  
  1510.  char *day[]={"Domingo",
  1511.  
  1512.  "Lunes",
  1513.  
  1514.  "Martes",
  1515.  
  1516.  "Miércoles",
  1517.  
  1518.  "Jueves",
  1519.  
  1520.  "Viernes",
  1521.  
  1522.  "Sábado"};
  1523.  
  1524.  static char *meses[]={"Enero",
  1525.  
  1526.  "Febrero",
  1527.  
  1528.  "Marzo",
  1529.  
  1530.  "Abril",
  1531.  
  1532.  "Mayo",
  1533.  
  1534.  "Junio",
  1535.  
  1536.  "Julio",
  1537.  
  1538.  "Agosto",
  1539.  
  1540.  "Septiembre",
  1541.  
  1542.  "Octubre",
  1543.  
  1544.  "Noviembre",
  1545.  
  1546.  "Diciembre",
  1547.  
  1548.  };
  1549.  
  1550.  time_t time_t;
  1551.  
  1552.  struct date date;
  1553.  
  1554.  struct tm*tm;
  1555.  
  1556.  getdate(&date);
  1557.  
  1558.  time_t=dostounix(&date,NULL);
  1559.  
  1560.  tm=localtime(&time_t);
  1561.  
  1562.  gotoxy(45,2);
  1563.  
  1564.  cprintf(" %s ",day[tm-> tm_wday]);
  1565.  
  1566.  cprintf("%02d",date.da_day);
  1567.  
  1568.  cprintf(" de %s",meses[tm-> tm_mon]);
  1569.  
  1570.  cprintf(" de %d\n",date.da_year);
  1571.  
  1572. }
  1573.  
  1574. Nota de la Laboratorio Técnico:
  1575.  
  1576. La fecha se visualiza en el estremo superior derecho de la pantalla,
  1577. para cambiar esto basta con modificar las coordenadas que aparecen en la
  1578. instrucción «gotoxy».
  1579.  
  1580. Jose Angel Vidal Gallego
  1581.  
  1582. Alacuas. Valencia
  1583.  
  1584.  
  1585. CRONOMETRO DE PROGRAMAS
  1586.  
  1587. El siguiente programa realizado en C, tiene la utilidad de cronometrar
  1588. el tiempo que está un programa funcionando. Fue mandado por su autor
  1589. como una aplicación para medir el tiempo que empleaba su programa en
  1590. escapar del laberento, dentro del concurso de programación, pero por
  1591. su utilidad lo hemos incluido dentro de esta sección.
  1592.  
  1593. #include <stdio.h>
  1594.  
  1595. #include <stdlib.h>
  1596.  
  1597. #include <conio.h>
  1598.  
  1599. #include <process.h>
  1600.  
  1601. #include <time.h>
  1602.  
  1603. void main()
  1604.  
  1605. {
  1606.  char prog[80];
  1607.  
  1608.  int r;
  1609.  
  1610.  clock_t antes, despues;
  1611.  
  1612.  double segs;
  1613.  
  1614.  printf( "Programa a cronometrar ? " );
  1615.  
  1616.  gets( prog );
  1617.  
  1618.  antes = clock();
  1619.  
  1620.  r = _spawnlp( _P_WAIT, prog, prog, NULL );
  1621.  
  1622.  despues = clock();
  1623.  
  1624.  segs = (double)(despues - antes) / CLOCKS_PER_SEC;
  1625.  
  1626.  if( r == -1 )
  1627.  
  1628.  printf( "\nNo se pudo ejecutar :\n%s" , prog );
  1629.  
  1630.  else
  1631.  
  1632.  printf( "\n%f segundos tarda en ejecutar :\n%s" , segs , prog );
  1633.  
  1634.  exit( r );
  1635.  
  1636. }
  1637.  
  1638. Joaquín Obregón Cobo
  1639.  
  1640. El Astillero. Cantabria
  1641.  
  1642.  
  1643. LA CLAVE DE LAS BIOS AMI
  1644.  
  1645. Aunque es una situación muy poco frecuente, algunos de nuestros
  1646. lectores se han encontrado con la desagradable sorpresa de que al
  1647. adquirir un ordenador con BIOS AMI (nuevo o de segunda mano) a
  1648. personas poco fiables, el setup del mismo se encontraba protegido con
  1649. una palabra clave que el vendedor se resistía a revelar. De esta
  1650. forma, algunas empresas se aseguran de que el usuario sólo podrá
  1651. modificar el hardware de su PC bajo su conocimiento, perdiendo la
  1652. garantía o bien pagando una cantidad extra por la instalación.
  1653.  
  1654. Tanto para este caso extremo como si simplemente se nos olvida nuestra
  1655. propia clave, puede resultar de utilidad la siguiente rutina, que
  1656. sirve para averiguar la password de las BIOS AMI. Para ello crearemos
  1657. en primer lugar un fichero llamado «crackami.dbg» con el siguiente
  1658. contenido:
  1659.  
  1660. a
  1661.  
  1662. jmp 118
  1663.  
  1664. db 37
  1665.  
  1666. mov al,[102]
  1667.  
  1668. out 70,al
  1669.  
  1670. jmp 10a
  1671.  
  1672. inc byte ptr [102]
  1673.  
  1674. cmp byte ptr [102],3f
  1675.  
  1676. je 13f
  1677.  
  1678. in al,71
  1679.  
  1680. ret
  1681.  
  1682. call 103
  1683.  
  1684. and al,f0
  1685.  
  1686. mov ah,al
  1687.  
  1688. call 103
  1689.  
  1690. or al,al
  1691.  
  1692. jz 13f
  1693.  
  1694. push ax
  1695.  
  1696. xor cl,cl
  1697.  
  1698. cmp al,ah
  1699.  
  1700. je 138
  1701.  
  1702. test al,e1
  1703.  
  1704. jpe 132
  1705.  
  1706. stc
  1707.  
  1708. rcl al,1
  1709.  
  1710. inc cl
  1711.  
  1712. jnz 129
  1713.  
  1714. mov al,cl
  1715.  
  1716. int 29
  1717.  
  1718. pop ax
  1719.  
  1720. jmp 11d
  1721.  
  1722. xor al,al
  1723.  
  1724. ret
  1725.  
  1726. db '// 1.1 // ARM '
  1727.  
  1728. <línea en blanco>
  1729.  
  1730. rcx
  1731.  
  1732. 50
  1733.  
  1734. n crackami.com
  1735.  
  1736. w
  1737.  
  1738. q
  1739.  
  1740. <línea en blanco>
  1741.  
  1742. A continuación damos la orden «DEBUG <crackami.dbg» para generar el
  1743. fichero ejecutable. Al lanzar el programa «CRACKAMI.COM» así creado
  1744. se imprimirá en pantalla la palabra clave almacenada actualmente en la
  1745. memoria CMOS de nuestro ordenador.
  1746.  
  1747. Antes de finalizar, hemos de hacer dos serias advertencias. La
  1748. primera es que AMI ha modificado el método de encriptación de la clave
  1749. en sus BIOS más recientes, las de 1994 (y en algunas del 93), de modo
  1750. que en estos casos no funcionará la presente rutina. La segunda y más
  1751. importante es que este programa sólo puede ser utilizado con el propio
  1752. ordenador: es un delito emplearlo en otra máquina sin autorización
  1753. expresa de su dueño, con el fin de tener acceso a la información
  1754. almacenada.
  1755.  
  1756. Arturo Ramírez-Montesinos Krogulski
  1757.  
  1758. Boadilla del Monte (Madrid)
  1759.  
  1760. Nota: Si el programa es incapaz de averiguar la clave almacenada en
  1761. la CMOS, habremos de recurrir a un método «artesanal». Para ello
  1762. llevaremos a cabo lo que se conoce como cortocircuitar la placa base,
  1763. uniendo mediante un cable las dos patillas de la batería de la CMOS.
  1764. Esto tiene como efecto inmediato la descarga total de todos los datos
  1765. almacenados en la propia CMOS (parámetros de disco duro, memoria y
  1766. disqueteras incluidas). En ciertas placas base basta con variar la
  1767. posición de un jumper para llevar a cabo esta descarga.
  1768.  
  1769.  
  1770. CALLAR A LA SOUND BLASTER
  1771.  
  1772. Este truco está dirigido a quienes posean una Sound Blaster y cuyo
  1773. sonido hubieran deseado anular en alguna ocasión sin tener que
  1774. recurrir al potenciómetro del amplificador. La siguiente rutina,
  1775. escrita en Borland C++ 2.0, permite reducir el volumen de dicha
  1776. tarjeta usando la tecla Scroll Lock (o Bloq Despl).
  1777.  
  1778. El funcionamiento del programa es muy sencillo. En primer lugar se
  1779. accede al chip del mezclador (puerto 224h) y se selecciona el registro
  1780. número 3, que es el que controla el volumen de los dos altavoces de la
  1781. Sound Blaster. Luego, mediante el puerto de datos del chip, se reduce
  1782. el volumen hasta cero o hasta «Ah» (este valor se puede cambiar a
  1783. gusto del usuario) según el estado de la tecla Bloq Despl. Tras ello
  1784. se deja el programa residente en memoria.
  1785.  
  1786. El puerto base de la Sound Blaster que se utiliza en este programa es
  1787. el 220h. Pero hay algunas tarjetas que utilizan el puerto 240h. En
  1788. este caso, se deberá modificar el valor de la variable «BASE».
  1789.  
  1790. #include "dos.h"
  1791.  
  1792. void interrupt (*oldkbd)();
  1793.  
  1794. void interrupt handler();
  1795.  
  1796. void interrupt handler()
  1797.  
  1798. {
  1799.  
  1800.  int tecla
  1801.  
  1802.  unsigned int BASE = 0x220;
  1803.  
  1804.  //Puerto base por defecto de la SB
  1805.  
  1806.  unsigned int CHIPBASE = BASE + 4;
  1807.  
  1808.  tecla = peek (0x0040, 0x0017);
  1809.  
  1810.  // Lee el estado del teclado
  1811.  
  1812.  if (tecla & 16)
  1813.  
  1814.  // Si la tecla Bloq Despl está encendida
  1815.  
  1816.  {
  1817.  
  1818.  asm {
  1819.  
  1820.  mov dx, CHIP
  1821.  
  1822.  // Puerto de registros del chip mezclador
  1823.  
  1824.  mov al, 3 // Volumen Master
  1825.  
  1826.  out dx, al // Seleccionar registro
  1827.  
  1828.  inc dx
  1829.  
  1830.  xor al, al // Reduce volumen
  1831.  
  1832.  out dx, al
  1833.  
  1834.  }
  1835.  
  1836.  }
  1837.  
  1838.  Else
  1839.  
  1840.  // Si está apagada...
  1841.  
  1842.  {
  1843.  
  1844.  asm {
  1845.  
  1846.  mov dx, CHIP
  1847.  
  1848.  mov al, 3
  1849.  
  1850.  out dx, al
  1851.  
  1852.  inc dx // Puerto de datos del chip mezclador
  1853.  
  1854.  xor al, 0ah // Aumento el volumen
  1855.  
  1856.  out dx, al
  1857.  
  1858.  }
  1859.  
  1860.  }
  1861.  
  1862.  oldkbd (); // Llamo a la antigua interrupción
  1863.  
  1864. }
  1865.  
  1866. void main (int argc, char **argv)
  1867.  
  1868. {
  1869.  
  1870.  oldkbd=getvect(0x9);
  1871.  
  1872.  setvect(0x9, handler);
  1873.  
  1874.  printf("\nSound Blaster PRO Volume");
  1875.  
  1876.  printf("\n========================");
  1877.  
  1878.  printf("\n Programa en memoria.");
  1879.  
  1880.  printf("\n========================\n");
  1881.  
  1882.  keep(0, (_SS+(_SP/16)- _psp)); //Instala el programa
  1883.  
  1884. }
  1885.  
  1886.  
  1887. Eduard Sánchez Palazón
  1888.  
  1889. Terrassa (Barcelona)
  1890.  
  1891.  
  1892. El UTIL XCOPY
  1893.  
  1894. Muchas veces se nos ha presentado el problema de tener que copiar del
  1895. disco duro a una unidad de disquete, sobrepasando con lo que deseamos
  1896. copiar el espacio del disco, teniendo que recurrir a varios disquetes
  1897. y efectuar la copia fichero a fichero para evitar la tan temida frase
  1898. de «espacio insuficiente».
  1899.  
  1900. Para remediar este problema, el mismo sistema operativo nos ofrece una
  1901. solución:
  1902.  
  1903. Primero ponemos el atributo de archivo a todos los ficheros que
  1904. deseemos copiar a disquete:
  1905.  
  1906. Por ejemplo: «Attrib c:\graficos\*.* +A»
  1907.  
  1908. Si queremos copiar también los subdirectorios podemos añadir el
  1909. parámetro /s
  1910.  
  1911. Introducir el disco en la unidad destino y escribir la orden de copia.
  1912. Por ejemplo:
  1913.  
  1914. «xcopy c:\graficos\*.* /m»
  1915.  
  1916. Con el parámetro /m conseguimos que se vaya modificando el atributo de
  1917. archivo segun se copian los diferentes ficheros.
  1918.  
  1919. Ejecutaremos este último paso y el ordenador empezará a copiar hasta
  1920. que no haya más espacio en el disco destino; entonces mostrará el
  1921. mensaje de «espacio insuficiente en disco» en cuyo caso sustituiremos
  1922. el disquete y repetiremos de nuevo el último paso, con lo que
  1923. continuará copiando en el archivo en que se quedó. Si repetimos este
  1924. paso cuantas veces sea necesario habremos copiado todos los ficheros
  1925. que deseabamos.
  1926.  
  1927. Joaquín Dionisio Sánchez
  1928.  
  1929. Málaga
  1930.  
  1931.  
  1932. INTERRUPCIONES CON QUICKBASIC
  1933.  
  1934. El uso de interrupciones a la hora de realizar cualquier programa es
  1935. una buena técnica para aumentar el grado de profesionalidad en éstos,
  1936. ya que así podremos emplear servicios de la ROM-BIOS y del DOS tales
  1937. como incluir soporte para ratón, controlar el subsistema gráfico, etc.
  1938.  
  1939. Como sabéis, el uso de interrupciones resulta más fácil de llevar a la
  1940. práctica en unos lenguajes que en otros, aunque no siempre se deba a
  1941. limitaciones del propio lenguaje. En el caso de QuickBasic 4.5 parece
  1942. que la causa reside más bien en la falta de documentación acerca de
  1943. cómo acceder a este tipo de servicios.
  1944.  
  1945. Desde tierras levantinas nos ha llegado la solución al problema, de
  1946. modo que quienes deseen utilizar las interrupciones en sus programas
  1947. simplemente tendrán que seguir los pasos que a continuación
  1948. describimos. En primer lugar, hay que crear un archivo ASCII, llamado
  1949. «intxs.bi», con el siguiente contenido:
  1950.  
  1951. TYPE tReg
  1952.  
  1953.  ax AS INTEGER
  1954.  
  1955.  bx AS INTEGER
  1956.  
  1957.  cx AS INTEGER
  1958.  
  1959.  dx AS INTEGER
  1960.  
  1961.  bp AS INTEGER
  1962.  
  1963.  si AS INTEGER
  1964.  
  1965.  di AS INTEGER
  1966.  
  1967.  flags AS INTEGER
  1968.  
  1969.  ds AS INTEGER
  1970.  
  1971.  es AS INTEGER
  1972.  
  1973. END TYPE
  1974.  
  1975. DIM SHARED rEn AS tReg
  1976.  
  1977. DIM SHARED rFue AS tReg
  1978.  
  1979. DECLARE SUB INTERRUPTX(num AS INTEGER, rEn AS tReg, rFue AS tReg)
  1980.  
  1981. A continuación, iniciaremos QuickBasic con el parámetro «/l» (por
  1982. ejemplo, «C:\QB45\QB /l»).
  1983.  
  1984. Al principio del programa en el que queramos acceder a alguna
  1985. interrupción añadiremos la orden «$INCLUDE: 'intxs.bi'». (El archivo
  1986. «intxs.bi» debe estar en el subdirectorio donde esté el QuickBasic).
  1987.  
  1988. Para realizar una interrupción desde cualquier punto del programa
  1989. basta con ejecutar la orden «CALL INTERRUPTX(num, rEn, rFue)», siendo
  1990. «num» el número del servicio que queremos usar.
  1991.  
  1992. Por lo general, tendremos que «pasar» unos parámetros para indicar qué
  1993. función queremos usar del servicio y otros datos. En ciertas
  1994. ocasiones también será necesario obtener el resultado de la llamada al
  1995. servicio. Los parámetros de entrada se tienen que dejar en la
  1996. variable «rEn», mientras que los de salida se obtienen o recogen en
  1997. «rFue».
  1998.  
  1999. A continuación presentamos un esquema general:
  2000.  
  2001.  
  2002. rem parámetros de entrada (si son necesarios)
  2003.  
  2004. rEn.ax = &Hxxxx
  2005.  
  2006. rEn.bx = &Hyyyy
  2007.  
  2008. rEn.cx = &Hzzzz
  2009.  
  2010. rem llamada al servicio
  2011.  
  2012. CALL INTERRUPTX(&Hxx,rEn,rFue)
  2013.  
  2014. rem evaluación de la salida (si es necesario)
  2015.  
  2016. IF rFue.ax=nn THEN <acciones>
  2017.  
  2018. PRINT rFue.bx
  2019.  
  2020.  
  2021. Hay que señalar que el método tiene el inconveniente de la
  2022. imposibilidad de separar los registros AX, BX, CX y DX en sus bytes
  2023. respectivos (AH, AL; BH, BL;...), por lo que nos veremos obligados a
  2024. trabajar con el registro entero. También hay que advertir que la
  2025. forma más cómoda de trabajar es en hexadecimal.
  2026.  
  2027. Veamos a continuación cómo utilizarlo. Supongamos que tenemos que
  2028. pasar al servicio los siguientes parámetros de entrada: «AL = x» y
  2029. «AH = y».
  2030.  
  2031. En primer lugar convertimos «x» e «y» a hexadecimal:
  2032.  
  2033. «x» lo convertimos en &Hmm, e «y» lo convertimos &Hnn
  2034.  
  2035. (de forma que «mm» y «nn» sean de 2 dígitos, rellenando con un «0» por
  2036. la izquierda si es necesario).
  2037.  
  2038. Los parámetros de entrada serán «rEn = &Hnnmm».
  2039.  
  2040. Si algún byte no hay que usarlo lo llenamos con «00». Es decir, si
  2041. por ejemplo tenemos que pasarle en BL el valor 10, la instrucción será
  2042. «rEn.bx=&H000A».
  2043.  
  2044. De igual forma, a la hora de obtener los resultados en «rFue» habrá
  2045. que separar en AH, AL; BH, BL; ... El procedimiento para ello es el
  2046. siguiente: convertimos el registro a separar (por ejemplo BX) a
  2047. hexadecimal; obtendremos entonces 4 dígitos (si son menos se rellena
  2048. por la izquierda con «0» hasta que sean 4). Los dos primeros, por la
  2049. derecha, corresponderán a BH y los dos siguientes a BL. Una vez
  2050. separados los podemos transformar a su vez en decimal o binario según
  2051. nuestras necesidades.
  2052.  
  2053. Sergio Ballester Lorente
  2054.  
  2055. Godella. (Valencia)
  2056.  
  2057.  
  2058. ALARMA RESIDENTE
  2059.  
  2060. Delante del ordenador las horas pasan muy rápidamente y por eso
  2061. es aconsejable disponer de un reloj con alarma a mano. ¡Qué mejor
  2062. reloj que el interno del ordenador! Este programa es una alarma que
  2063. avisará de la hora indicada con unos pitidos. Se queda residente y
  2064. emplea muy poca memoria para ello. Está realizado en ensamblador y
  2065. podemos emplear el «debug» para compilarlo.
  2066.  
  2067. a
  2068.  
  2069. mov ax,2528
  2070.  
  2071. mov dx,0117
  2072.  
  2073. int 21
  2074.  
  2075. mov ah,49
  2076.  
  2077. mov es,[2c]
  2078.  
  2079. int 21
  2080.  
  2081. mov ah,31
  2082.  
  2083. mov dx,14
  2084.  
  2085. int 21
  2086.  
  2087. nop
  2088.  
  2089. nop
  2090.  
  2091. push ax
  2092.  
  2093. push bx
  2094.  
  2095. push cx
  2096.  
  2097. push dx
  2098.  
  2099. mov ah,02
  2100.  
  2101. int 1a
  2102.  
  2103. cmp ch,05 ;Horas
  2104.  
  2105. jne 139
  2106.  
  2107. cmp cl,05 ;minutos
  2108.  
  2109. jne 139
  2110.  
  2111. cmp dh,02 ;segundos
  2112.  
  2113. jne 139
  2114.  
  2115. mov cx,04
  2116.  
  2117. mov al,07
  2118.  
  2119. mov ah,0e
  2120.  
  2121. int 10
  2122.  
  2123. pop dx
  2124.  
  2125. pop cx
  2126.  
  2127. pop bx
  2128.  
  2129. pop ax
  2130.  
  2131. iret
  2132.  
  2133. <Línea en blanco>
  2134.  
  2135. rcx
  2136.  
  2137. 3e
  2138.  
  2139. n alarma.com
  2140.  
  2141. w
  2142.  
  2143. q
  2144.  
  2145.  
  2146. Gontzal Uriarte Gómez
  2147.  
  2148. San Juan (Alicante)
  2149.  
  2150.  
  2151. FICHEROS «BATCH»
  2152.  
  2153. Muchos usuarios habrán necesitado en más de una ocasión que un fichero
  2154. batch envíe su salida a la impresora o a un fichero de texto, y
  2155. lógicamente habrán hecho lo siguiente:
  2156.  
  2157. Fbatch > Salida.prn
  2158.  
  2159. Suponiendo que el fichero se llama «Fbatch.bat» y se desea volcar la
  2160. salida al fichero «Salida.prn».
  2161.  
  2162. Pero el funcionamiento interno del DOS no lo permite (al contrario que
  2163. en otros sistemas operativos como Unix), ya que sólo se redirecciona
  2164. la salida de programas «.COM» o «.EXE» que utilicen la salida
  2165. estándar.
  2166.  
  2167. La solución está en emular el comportamiento de sistemas operativos
  2168. como el Unix. Veamos qué hace el Unix cuando lanza un fichero script
  2169. (batch para MS-DOS). Lo primero que hace es cargar un nuevo shell que
  2170. ejecute los comandos del fichero y redireccionar la salida donde se
  2171. haya especificado o a la consola por defecto. Pues bien, esto
  2172. traducido en MS-DOS sería:
  2173.  
  2174. Command /C Fbatch > Salida.prn
  2175.  
  2176. En realidad se le está diciendo que se cargue un nuevo shell
  2177. («command.com») y se redireccione la salida estándar de éste al
  2178. fichero «Salida.prn», pero que únicamente se ejecuten los comandos que
  2179. hay en «Fbatch.bat» y regrese al shell anterior (parámetro /C).
  2180.  
  2181. Gabriel Martí
  2182.  
  2183. Barcelona
  2184.  
  2185.  
  2186.  
  2187.  
  2188.  
  2189.  
  2190.  
  2191.